<script setup>
import { ref, onMounted } from 'vue'
import { Map, Overlay, View } from 'ol'              // Map：地图容器，Overlay：叠加层，View：视图控制
import TileLayer from 'ol/layer/Tile'                // 瓦片图层
import XYZ from 'ol/source/XYZ'                      // XYZ数据源（用于加载天地图等瓦片服务）
import VectorSource from 'ol/source/Vector'          // 矢量数据源（用于存储用户绘制的几何图形）
import VectorLayer from 'ol/layer/Vector'            // 矢量图层（用于显示矢量数据）
import Style from 'ol/style/Style'                   // 样式定义
import { Fill } from 'ol/style'                      // 填充样式
import { Circle, Stroke } from 'ol/style'            // 圆形和边框样式
import { LineString, Polygon } from 'ol/geom'        // 几何对象：线和面
import Draw from 'ol/interaction/Draw'               // 绘制交互工具
import * as sphere from 'ol/sphere'                  // 球面计算工具（用于测量距离和面积）
import { unByKey } from 'ol/Observable'              // 事件监听管理工具

// 响应式数据
const map = ref(null)             // 地图实例
const currentType = ref('')       // 当前测量类型：'Polygon'（面积）或'LineString'（距离）
const isMeasuring = ref(false)    // 是否正在测量
const source = ref(null)          // 矢量数据源引用
const vectorLayer = ref(null)     // 矢量图层引用

// 内部状态变量
let draw = null;                  // 绘制交互实例
let sketch = null;                // 当前正在绘制的图形要素
let helpTooltipElement = null;    // 帮助提示框DOM元素
let helpTooltip = null;           // 帮助提示框叠加层
let measureTooltipElement = null; // 测量结果提示框DOM元素
let measureTooltip = null;        // 测量结果提示框叠加层
let listener = null;              // 几何图形变化事件监听器

// 提示信息文本
const continuePolygonMsg = '点击继续绘制多边形';
const continueLineMsg = '点击继续绘制线';

/**
 * 初始化地图
 * 创建地图实例并加载天地图瓦片服务
 */
const initMap = () => {
  map.value = new Map({
    target: 'map',  // 挂载到id为'map'的DOM元素
    layers: [
      new TileLayer({
        source: new XYZ({ 
          url: 'http://t4.tianditu.com/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=d30d1c34260f2edc72b25f9985e2fd69' 
        })
      })
    ],
    view: new View({
      projection: 'EPSG:4326',  // 使用WGS84坐标系
      zoom: 12,                 // 初始缩放级别
      center: [113.24981689453125, 23.126468438108688]  // 初始中心点坐标
    })
  });
}

/**
 * 开始测量
 * @param {string} measureType - 测量类型：'Polygon'（面积）或'LineString'（距离）
 */
const startMeasure = (measureType) => {
  // 清除之前的测量状态
  clearMeasure();
  
  // 设置当前测量状态
  currentType.value = measureType;
  isMeasuring.value = true;
  
  // 创建矢量数据源和图层
  source.value = new VectorSource();
  vectorLayer.value = new VectorLayer({
    source: source.value,
    style: new Style({
      fill: new Fill({ color: '#008c8c' }),
      stroke: new Stroke({ color: '#ffcc33', width: 2 }),
      image: new Circle({ radius: 7, fill: new Fill({ color: '#ffcc33' }) })
    })
  });
  
  // 添加图层到地图
  map.value.addLayer(vectorLayer.value);
  
  // 绑定鼠标移动事件，显示帮助信息
  map.value.on('pointermove', pointerMoveHandler);
  
  // 添加绘制交互
  addInteraction(measureType);
}

/**
 * 创建帮助提示框
 * 显示绘制过程中的操作提示
 */
const createHelpTooltip = () => {
  // 清理已存在的提示框
  if (helpTooltipElement) {
    helpTooltipElement.remove();
  }
  
  // 创建新的提示框元素
  helpTooltipElement = document.createElement('div');
  helpTooltipElement.className = 'tooltip';
  
  // 创建叠加层并添加到地图
  helpTooltip = new Overlay({
    element: helpTooltipElement,
    offset: [15, 0],      // 偏移量
    positioning: 'center-left'  // 定位方式
  });
  
  map.value.addOverlay(helpTooltip);
}

/**
 * 创建测量结果提示框
 * 显示实时测量结果
 */
const createMeasureTooltip = () => {
  // 清理已存在的提示框
  if (measureTooltipElement) {
    measureTooltipElement.remove();
  }
  
  // 创建新的提示框元素
  measureTooltipElement = document.createElement('div');
  measureTooltipElement.className = 'tooltip tooltip-measure';
  
  // 创建叠加层并添加到地图
  measureTooltip = new Overlay({
    element: measureTooltipElement,
    offset: [0, -15],     // 偏移量
    positioning: 'bottom-center'  // 定位方式
  });
  
  map.value.addOverlay(measureTooltip);
}

/**
 * 添加绘制交互
 * @param {string} measureType - 测量类型：'Polygon'（面积）或'LineString'（距离）
 * 
 * Draw交互是OpenLayers中用于绘制几何图形的核心工具
 * 它会根据指定的type参数创建对应的几何图形（线或面）
 */
const addInteraction = (measureType) => {
  // 创建绘制交互实例
  draw = new Draw({
    source: source.value,  // 绘制的图形会添加到这个数据源
    type: measureType,     // 绘制类型：'LineString'或'Polygon'
    style: new Style({
      // 绘制过程中的图形样式
      fill: new Fill({ color: 'rgba(255, 255, 255, 0.8)' }),
      stroke: new Stroke({ color: 'rgba(0, 0, 0, 0.5)', lineDash: [10, 10], width: 2 }),
      image: new Circle({ 
        radius: 5, 
        stroke: new Stroke({ color: 'rgba(0, 0, 0, 0.7)' }),
        fill: new Fill({ color: 'rgba(255, 255, 255, 0.5)' })
      })
    })
  });
  
  // 添加右键停止绘制功能
  map.value.on('contextmenu', (evt) => {
    evt.preventDefault();  // 阻止默认的右键菜单
    if (draw && sketch) {
      draw.finishDrawing();  // 手动结束当前绘制
    }
  });
  
  // 添加交互到地图
  map.value.addInteraction(draw);
  
  // 创建提示框
  createMeasureTooltip();
  createHelpTooltip();
  
  // 监听绘制开始事件
  draw.on('drawstart', (event) => {
    // 获取当前正在绘制的图形要素
    sketch = event.feature;
    let tooltipCoordinate = event.coordinate;
    
    // 监听几何图形变化事件，实时计算测量结果
    // 这里的geom是OpenLayers中的几何对象，包含线(LineString)或面(Polygon)等类型
    listener = sketch.getGeometry().on('change', (evt) => {
      const geom = evt.target;
      let output;
      
      // 根据几何类型计算面积或长度
      if (geom instanceof Polygon) {
        output = formatArea(geom);
        // 对于多边形，使用内部点作为提示框位置
        tooltipCoordinate = geom.getInteriorPoint().getCoordinates();
      } else if (geom instanceof LineString) {
        output = formatLength(geom);
        // 对于线，使用最后一个点作为提示框位置
        tooltipCoordinate = geom.getLastCoordinate();
      }
      
      // 更新测量结果显示
      measureTooltipElement.innerHTML = output;
      measureTooltip.setPosition(tooltipCoordinate);
    });
  });
  
  // 监听绘制结束事件
  draw.on('drawend', () => {
    // 更改提示框样式，表示测量完成
    measureTooltipElement.className = 'tooltip tooltip-measure tooltip-static';
    measureTooltip.setOffset([0, -7]);
    
    // 重置状态
    sketch = null;
    measureTooltipElement = null;
    createMeasureTooltip();
    unByKey(listener);  // 移除事件监听
  });
}

/**
 * 计算并格式化面积
 * @param {Polygon} polygon - OpenLayers的Polygon几何对象
 * @returns {string} 格式化后的面积字符串
 */
const formatArea = (polygon) => {
  const sourceProj = map.value.getView().getProjection();
  // 转换坐标系为EPSG:4326以提高测量精度
  const geom = polygon.clone().transform(sourceProj, 'EPSG:4326');
  
  // 使用球面计算获取面积（考虑地球曲率）
  const area = Math.abs(sphere.getArea(geom, {
    projection: sourceProj,
    radius: 6378137  // 地球半径（米）
  }));
  
  // 根据面积大小选择合适的单位
  let output;
  if (area > 10000) {
    output = `${(Math.round(area / 1000000 * 100) / 100).toFixed(2)} km<sup>2</sup>`;
  } else {
    output = `${(Math.round(area * 100) / 100).toFixed(2)} m<sup>2</sup>`;
  }
  
  return output;
}

/**
 * 计算并格式化长度
 * @param {LineString} line - OpenLayers的LineString几何对象
 * @returns {string} 格式化后的长度字符串
 */
const formatLength = (line) => {
  const sourceProj = map.value.getView().getProjection();
  
  // 使用球面计算获取长度（考虑地球曲率）
  const length = sphere.getLength(line, {
    projection: sourceProj,
    radius: 6378137  // 地球半径（米）
  });
  
  // 根据长度大小选择合适的单位
  let output;
  if (length > 100) {
    output = `${(Math.round(length / 1000 * 100) / 100).toFixed(2)} km`;
  } else {
    output = `${(Math.round(length * 100) / 100).toFixed(2)} m`;
  }
  
  return output;
}

/**
 * 处理鼠标移动事件，显示帮助信息
 * @param {Event} event - 鼠标事件对象
 */
const pointerMoveHandler = (event) => {
  if (event.dragging) return;  // 如果正在拖拽，不处理
  
  let helpMsg = '点击开始绘制！';
  
  // 根据当前绘制的几何类型显示不同的帮助信息
  if (sketch) {
    const geom = sketch.getGeometry();
    if (geom instanceof Polygon) {
      helpMsg = continuePolygonMsg;
    } else if (geom instanceof LineString) {
      helpMsg = continueLineMsg;
    }
  }
  
  // 更新帮助提示框内容和位置
  if (helpTooltipElement) {
    helpTooltipElement.innerHTML = helpMsg;
    helpTooltip.setPosition(event.coordinate);
  }
}

/**
 * 清除所有测量状态和元素
 */
const clearMeasure = () => {
  // 移除绘制交互
  if (draw) {
    map.value.removeInteraction(draw);
    draw = null;
  }
  
  // 移除矢量图层
  if (vectorLayer.value) {
    map.value.removeLayer(vectorLayer.value);
    vectorLayer.value = null;
  }
  
  // 清空数据源
  if (source.value) {
    source.value.clear();
    source.value = null;
  }
  
  // 清理提示框元素
  if (helpTooltipElement) {
    helpTooltipElement.remove();
    helpTooltipElement = null;
  }
  
  if (measureTooltipElement) {
    measureTooltipElement.remove();
    measureTooltipElement = null;
  }
  
  // 重置所有状态变量
  sketch = null;
  listener = null;
  currentType.value = '';
  isMeasuring.value = false;
}

// 组件挂载时初始化地图
onMounted(initMap);
</script>

<template>
  <div id="map-container">
    <div id="map"></div>
    <div class="measure-toolbar">
      <el-button type="primary" :class="{active: currentType === 'Polygon'}" @click.stop.prevent="startMeasure('Polygon')">
        <i class="el-icon-zoom-in"></i> 面积测量
      </el-button>
      <el-button type="success" :class="{active: currentType === 'LineString'}" @click.stop.prevent="startMeasure('LineString')">
        <i class="el-icon-rank"></i> 距离测量
      </el-button>
      <el-button type="danger" v-if="isMeasuring" @click.stop.prevent="clearMeasure">
        <i class="el-icon-delete"></i> 清除
      </el-button>
    </div>
  </div>
</template>

<style scoped>
#map-container {
  width: 100%;
  height: 100%;
  position: relative;
}

#map {
  width: 100%;
  height: 100%;
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  background-color: #fafafa;
}

.measure-toolbar {
  position: absolute;
  top: 15px;
  right: 15px;
  z-index: 9999;
  background: white;
  padding: 10px;
  border-radius: 12px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  display: flex;
  gap: 8px;
}

.measure-toolbar .el-button {
  transition: all 0.3s ease;
  border-radius: 8px;
  padding: 10px 20px;
  background: #ffffff;
  border: 2px solid #e0e0e0;
  cursor: pointer;
  font-size: 14px;
  font-weight: 500;
  color: #333;
  font-family: 'Microsoft YaHei', Arial, sans-serif;
}

.measure-toolbar .el-button:hover {
  background: #f8f9fa;
  border-color: #008c8c;
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(0, 140, 140, 0.15);
}

.measure-toolbar .el-button.active {
  transform: scale(1.05);
  box-shadow: 0 4px 12px rgba(0, 140, 140, 0.3);
  background: #008c8c;
  color: white;
  border-color: #008c8c;
}

.measure-toolbar .el-button:active {
  transform: translateY(0);
}

/* 清除按钮特殊样式 */
.measure-toolbar .el-button--danger {
  background: #f44336;
  color: white;
  border-color: #f44336;
}

.measure-toolbar .el-button--danger:hover {
  background: #d32f2f;
  box-shadow: 0 2px 8px rgba(244, 67, 54, 0.25);
}

/* 提示框样式 */
:deep(.tooltip) {
  position: relative;
  background: rgba(0, 0, 0, 0.7);
  border-radius: 6px;
  color: white;
  padding: 6px 12px;
  font-size: 13px;
  white-space: nowrap;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(2px);
  pointer-events: none;
  opacity: 0.9;
  transition: opacity 0.3s ease;
  font-family: 'Microsoft YaHei', Arial, sans-serif;
}

:deep(.tooltip-measure) {
  font-weight: bold;
  background: #008c8c;
  animation: tooltip-appear 0.3s ease-out;
}

:deep(.tooltip-static) {
  background-color: #ffcc33;
  color: #333;
  border: 2px solid #ffcc33;
  font-weight: bold;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  padding: 8px 12px;
  animation: tooltip-appear 0.3s ease-out;
}

:deep(.tooltip-measure:before),
:deep(.tooltip-static:before) {
  content: '';
  position: absolute;
  bottom: -8px;
  left: 50%;
  transform: translateX(-50%);
  border-top: 8px solid rgba(0, 0, 0, 0.7);
  border-right: 8px solid transparent;
  border-left: 8px solid transparent;
}

:deep(.tooltip-measure:before) {
  border-top-color: #008c8c;
}

:deep(.tooltip-static:before) {
  border-top-color: #ffcc33;
}

@keyframes tooltip-appear {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 响应式设计 */
@media (max-width: 768px) {
  .measure-toolbar {
    top: auto;
    bottom: 12px;
    left: 50%;
    transform: translateX(-50%);
    flex-wrap: wrap;
    justify-content: center;
    padding: 8px;
  }
  
  .measure-toolbar .el-button {
    padding: 8px 16px;
    font-size: 13px;
  }
  
  #map {
    height: 350px;
  }
}
</style>